package web

import (
	"embed"
	"errors"
	"gitee.com/go-boot/boot-http/interceptor"
	"gitee.com/go-boot/boot-interfaces/ihttp"
	"gitee.com/go-boot/boot-interfaces/ilogger"
	logger "gitee.com/go-boot/boot-logger"
	"net/http"
	"os"
	"os/signal"
	"sort"
	"strings"
	"sync"
	"syscall"
)

type (
	// QuickWebEngine web引擎
	HttpEngine struct {
		log               ilogger.ILogger
		Port              int64                      // 端口
		tree              map[string]ihttp.IRouter   // 路由tree
		pool              sync.Pool                  // 上下文对象池
		interceptors      []ihttp.IRouterInterceptor // 拦截器
		basePath          string                     // 基础路径
		notfoundPath      ihttp.HandlerFunc          // 路由没有找到
		beforeInterceptor ihttp.HandlerFunc          // 前置拦截器拦截处理器
		afterInterceptor  ihttp.HandlerFunc          // 后置拦截器拦截处理器
	}
)

// ServeHTTP Handler
func (engine *HttpEngine) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	// 初始化上下文
	ctx := engine.getPool()
	ctx.Build(response, request)
	// 匹配路径对应的handler
	_ = engine.matchPath(ctx)
	engine.putPool(ctx)
}

func (engine *HttpEngine) Match(ctx ihttp.IHttpContext) ihttp.HandlerFunc {
	var (
		path             = ctx.Path()
		method           = ctx.Method()
		params           = new(HttpRouterParams)
		pathArray        []string
		findRouter       func(int, []string, *HttpRouterParams, ihttp.IRouter) (ihttp.IRouter, error)
		findStaticRouter func(path string, router ihttp.IRouter) (ihttp.IRouter, error)

		router  ihttp.IRouter
		handler ihttp.HandlerFunc
		exist   bool
		err     error
	)
	// 递归查找路由节点
	findRouter = func(i int, paths []string, params *HttpRouterParams, router ihttp.IRouter) (ihttp.IRouter, error) {
		if router.Path() == paths[i] || strings.HasPrefix(router.Path(), ":") {
			if strings.HasPrefix(router.Path(), ":") {
				params.params = append(params.params, HttpRouterParam{key: router.Path()[1:], value: paths[i]})
			}
			if i+1 >= len(paths) {
				return router, nil
			}
			for _, child := range router.Children() {
				if foundRouter, err := findRouter(i+1, paths, params, child); err == nil {
					return foundRouter, nil
				}
			}
		}
		return nil, errors.New("not found path")
	}
	findStaticRouter = func(path string, router ihttp.IRouter) (ihttp.IRouter, error) {
		return router, nil
	}

	pathArray = strings.Split(path[1:], "/")
	if router, err = findRouter(0, pathArray, params, engine.tree["api"]); err != nil {
		if router, err = findStaticRouter(path, engine.tree["static"]); err != nil {
			engine.log.WarnF("没有匹配的请求路由 [%s:%s]", method, path)
			return engine.notfoundPath
		}
	}
	// 是否存在对应的Handler
	if handler, exist = router.Handler(method); !exist {
		engine.log.WarnF("路由请求方式不支持 [%s:%s]", method, path)
		return engine.notfoundPath
	}
	ctx.Request().SetParam(params)
	return handler
}

// Interceptor 添加拦截器
func (engine *HttpEngine) Interceptor(interceptor ...ihttp.IRouterInterceptor) {
	engine.interceptors = append(engine.interceptors, interceptor...)
	// Order越小越先执行
	sort.Slice(engine.interceptors, func(i, j int) bool {
		return engine.interceptors[i].Order() < engine.interceptors[j].Order()
	})
}

// Start 启动
func (engine *HttpEngine) Start(handlers ...func()) {
	engine.printUrlPath()
	engine.printInterceptor()
	go func() {
		engine.log.InfoF("启动HTTP服务器[%s]", engine.port())
		if err := http.ListenAndServe(engine.port(), engine); err != nil {
			engine.log.FailF("启动服务器失败:%s", err.Error())
			panic(err)
		}
	}()
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	<-quit
	for _, handler := range handlers {
		handler()
	}
	engine.log.Info("服务停止中......")
}

// Routes 构建路由表
func (engine *HttpEngine) Routes(basePath ...string) ihttp.IRouter {
	if len(basePath) > 0 {
		engine.basePath = basePath[0]
	} else {
		engine.basePath = ""
	}
	router := NewRouter(engine.basePath)
	engine.tree["api"] = router
	return router
}

func (engine *HttpEngine) StaticEmbed(urlPath string, filepath string, embed embed.FS) {
	engine.tree["static"] = NewStaticRouter(urlPath, filepath, embed)
}

// Default 默认
func Default() *HttpEngine {
	return &HttpEngine{
		log:          logger.DefaultLogger(),
		Port:         8080,
		tree:         make(map[string]ihttp.IRouter),
		interceptors: []ihttp.IRouterInterceptor{interceptor.NewLogger()},
		notfoundPath: func(ctx ihttp.IHttpContext) {
			ctx.Log().ErrorF("资源[%s:%s]没有找到", ctx.Method(), ctx.Path())
		},
	}
}

func NewHttp(log ilogger.ILogger, port int64) *HttpEngine {
	return &HttpEngine{
		log:          log,
		Port:         port,
		tree:         make(map[string]ihttp.IRouter),
		interceptors: []ihttp.IRouterInterceptor{interceptor.NewLogger()},
		notfoundPath: func(ctx ihttp.IHttpContext) {
			ctx.Log().ErrorF("资源[%s:%s]没有找到", ctx.Method(), ctx.Path())
		},
	}
}
